package edu.jhu.cs.oose.scheduleit.services.impl;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import edu.jhu.cs.oose.scheduleit.dao.ScheduleDao;
import edu.jhu.cs.oose.scheduleit.dao.UserScheduleDao;
import edu.jhu.cs.oose.scheduleit.services.EventService;
import edu.jhu.cs.oose.scheduleit.services.ScheduleService;
import edu.jhu.cs.oose.scheduleit.util.Attribute;
import edu.jhu.cs.oose.scheduleit.util.Comparator;
import edu.jhu.cs.oose.scheduleit.util.Filter;
import edu.jhu.cs.oose.scheduleit.util.ScheduleAttributeKind;
import edu.jhu.cs.oose.scheduleit.util.ScheduleType;
import edu.jhu.cs.oose.scheduleit.vo.Event;
import edu.jhu.cs.oose.scheduleit.vo.Schedule;
import edu.jhu.cs.oose.scheduleit.vo.User;
import edu.jhu.cs.oose.scheduleit.vo.UserSchedule;

/**
 * The implementation class of the ScheduleService interface
 * @author oosegroup2
 *
 */
public class ScheduleServiceImpl implements ScheduleService {

	
	/**
	 * The data access object related to schedule
	 */
	private ScheduleDao scheduleDao;

	/**
	 * The service for event
	 */
	private EventService eventService;

	/**
	 * The data access object related to UserSchedule
	 */
	private UserScheduleDao userScheduleDao;

	/**
	 * The setter for scheduleDao
	 * 
	 * @param scheduleDao
	 */
	public void setScheduleDao(ScheduleDao scheduleDao) {
		this.scheduleDao = scheduleDao;
	}

	/**
	 * The setter for eventService
	 * 
	 * @param eventService
	 */
	public void setEventService(EventService eventService) {
		this.eventService = eventService;
	}

	/**
	 * The setter for userScheduleDao
	 * 
	 * @param userScheduleDao
	 */
	public void setUserScheduleDao(UserScheduleDao userScheduleDao) {
		this.userScheduleDao = userScheduleDao;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Schedule createSchedule(User user, Attribute attribute)
			throws DataBaseException {
		try {
			Schedule schedule = new Schedule();
			schedule.setHost(user.getUserInfo());
			Object value = attribute.getValue(ScheduleAttributeKind.clicks);
			if (value != null) {
				if (value instanceof Integer) {
					schedule.setClicks((Integer) value);
				} else {
					schedule.setClicks((Long) value);
				}
			}else{
				schedule.setClicks(0);
			}
			
			value = attribute.getValue(ScheduleAttributeKind.editable);
			if (value != null){
				schedule.setEditable((Boolean)value);
			}else{
				schedule.setEditable(false);
			}
			
			value = attribute.getValue(ScheduleAttributeKind.publicAccess);
			if (value != null){
				schedule.setPublicAccess((Boolean)value);
			}else{
				schedule.setPublicAccess(false);
			}
			
			value = attribute.getValue(ScheduleAttributeKind.tags);
			List<String> tags = new LinkedList<String>();
			if (value != null){
				tags.addAll((Collection<String>)value);
			}
			schedule.setTags(tags);
			
			value = attribute.getValue(ScheduleAttributeKind.topic);
			if (value != null){
				schedule.setTopic((String) value);
			}else{
				schedule.setTopic("");
			}
			
			value = attribute.getValue(ScheduleAttributeKind.mergedSchedules);
			
			if (value != null) {
				for (Schedule s : (Collection<Schedule>) value) {
					schedule.addMergedSchedule(s);
				}
			}
			this.scheduleDao.save(schedule);

			// Create Relationship between user and schedule
			UserSchedule userSchedule = new UserSchedule(user, schedule,
					(ScheduleType) attribute
							.getValue(ScheduleAttributeKind.type));
			this.userScheduleDao.save(userSchedule);
			return schedule;
		} catch (Exception e) {
			e.printStackTrace();
			throw new DataBaseException(e);
		}
	}

	public Schedule createSchedule(User user, List<Schedule> mergedSchedules) throws DataBaseException{
		List<Schedule> tmp = this.getSchedule(user, ScheduleType.MERGED, 0, 0);
		int number = 1;
		if (tmp.size()>0){
			String[] s = tmp.get(tmp.size()-1).getTopic().split("#");
			number = Integer.parseInt(s[1])+1;
		}
		Attribute attribute = new Attribute();
		attribute.put(ScheduleAttributeKind.editable, true);
		attribute.put(ScheduleAttributeKind.publicAccess, false);
		attribute.put(ScheduleAttributeKind.clicks, 0);
		attribute.put(ScheduleAttributeKind.topic, "Merged #"+number);
		attribute.put(ScheduleAttributeKind.type, ScheduleType.MERGED);
		attribute.put(ScheduleAttributeKind.tags, new LinkedList<String>());
		attribute.put(ScheduleAttributeKind.mergedSchedules,mergedSchedules);
		return this.createSchedule(user, attribute);
	}
	@Override
	public void addSubscribedSchedule(User user, Schedule subscribedSchedule)
			throws DataBaseException {
		try {
			UserSchedule userSchedule = new UserSchedule(user,
					subscribedSchedule, ScheduleType.SUBSCRIBED);
			this.userScheduleDao.save(userSchedule);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public void removeSchedule(Schedule schedule) throws DataBaseException {
		try {
			List<Event> events = this.eventService.getEvents(schedule);
			for (Event event : events) {
				this.eventService.removeEvent(schedule, event);
			}
			this.userScheduleDao.delete(schedule);
			this.scheduleDao.delete(schedule);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public void updateSchedule(Schedule schedule) throws DataBaseException {
		try {
			if (schedule.getType() != ScheduleType.SUBSCRIBED) {
				this.scheduleDao.save(schedule);
			}
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public List<Schedule> getSchedule(User user, ScheduleType type, int offset,
			int limit) throws DataBaseException {
		try {
			return this.scheduleDao.get(user, type, offset, limit);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public List<Schedule> getSchedule(List<Filter> filters, int offest,
			int limit) throws DataBaseException {
		try {
			filters.add(new Filter(ScheduleAttributeKind.publicAccess, Comparator.EQ,(Object)Boolean.TRUE));
			return this.scheduleDao.get(filters, offest, limit);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	public int getScheduleCount(User user, ScheduleType type)
			throws DataBaseException {
		try {
			return this.scheduleDao.count(user, type);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public Schedule getSchedule(User user, int scheduleID)
			throws DataBaseException {
		try {
			return this.scheduleDao.get(user, scheduleID);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public int getScheduleCount(List<Filter> filters)
			throws DataBaseException {
		try {
			filters.add(new Filter(ScheduleAttributeKind.publicAccess, Comparator.EQ,(Object)Boolean.TRUE));
			return this.scheduleDao.count(filters);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}
}
